﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Red.Controls.ViewModels;

namespace Red.Controls.Diagrams.InteropUtils
{
    public abstract class InteropProperty : IProperty
    {
        #region Data

        protected string _propertyName;
        protected DataItem _dataItem;
        protected bool _isBinding;
        protected string _type;

        #endregion Data

        #region Public methods

        public abstract void SetValue( string value, string operationID = "" );

        #endregion Public methods

        #region Protected methods

        protected abstract Dictionary<string, string> GetValues();

        #endregion Protected methods

        #region IProperty implementation

        public string Name
        {
            get { return _propertyName; }
        }

        public string ID
        {
            get { return _propertyName; }
        }

        public Dictionary<string, string> Values
        {
            get { return GetValues(); }
        }

        public new string GetType()
        {
            return _type;
        }

        public bool IsCompatibleWith( IProperty otherProperty )
        {
            throw new Exception( "Use IsCompatibleWithAsync for interop properties!" );
        }

        public async Task<bool> IsCompatibleWithAsync( IProperty otherProperty )
        {
            // we need to add "handle:" to type name because all binding properties are handles to objects.
            var res = await _dataItem.ExecuteAsync<bool>( "canCreateConnection", _propertyName, $"handle:{otherProperty.GetType()}" );
            return res.Value;
        }

        public bool IsArray()
        {
            return _dataItem.Kind == DataItemKind.Container;
        }

        public bool IsObject()
        {
            return ( _dataItem as InteropDataItem ).Data.IsCompound;
        }

        public bool IsBinding()
        {
            return _isBinding;
        }

        #endregion IProperty implementation
    }

    public class InteropEditorOnlyProperty : InteropProperty
    {
        #region C-tor

        public InteropEditorOnlyProperty( string propertyName, DataItem dataItem )
        {
            _dataItem = dataItem;
            _propertyName = propertyName;
            _isBinding = false;
            _type = _dataItem.Type;
        }

        #endregion C-tor

        #region InteropProperty implementation

        public override void SetValue( string value, string operationID = "" )
        {
            _dataItem.Value = value;
        }

        protected override Dictionary<string, string> GetValues()
        {
            //
            // TODO: 
            // currently we are assuming that one connector leads to only one node
            // In the future add support for array properties
            //
            var values = new Dictionary<string, string> { { "", _dataItem.Value } };
            return values;
        }

        #endregion InteropProperty implementation
    }

    public class InteropBindingProperty : InteropProperty
    {
        #region Data

        private string _value;

        #endregion Data

        #region C-tor

        public InteropBindingProperty( string propertyName, string value, DataItem dataItem, string type )
        {
            _value = value;
            _propertyName = propertyName;
            _dataItem = dataItem;
            _isBinding = true;
            // all blocks in graph are set on properties that are handles, we don't need this information here, only type
            _type = type.Replace( "handle:", "" );      
        }

        #endregion C-tor

        #region InteropProperty implementation

        public override void SetValue( string value, string operationID = "" )
        {
            _value = value;

            if ( string.IsNullOrEmpty( operationID ) )
                operationID = Guid.NewGuid().ToString();

            if ( string.IsNullOrEmpty( value ) )
                _dataItem.Execute( "removeConnection", _propertyName, operationID );
            else
                _dataItem.Execute( "createConnection", _propertyName, value, operationID );
        }

        protected override Dictionary<string, string> GetValues()
        {
            //
            // TODO: 
            // currently we are assuming that one connector leads to only one node
            // In the future add support for array properties
            //
            var values = new Dictionary<string, string> { { "", _value } };
            return values;
        }

        #endregion InteropProperty implementation
    }

    public class InputProperty : IProperty
    {
        #region Data

        private readonly IObject _iObject;
        private readonly string _propertyName;

        #endregion Data

        #region C-tor

        public InputProperty( IObject iObject, string propertyName )
        {
            _iObject = iObject;
            _propertyName = propertyName;
        }

        #endregion C-tor

        #region IProperty implementation

        public string ID => _iObject.ID;

        public string Name => _propertyName;

        public Dictionary<string, string> Values => new Dictionary<string, string>();

        public new string GetType()
        {
            return _iObject.Class;
        }

        public bool IsCompatibleWith( IProperty otherProperty )
        {
            return otherProperty.IsCompatibleWith( this );
        }

        public async Task<bool> IsCompatibleWithAsync( IProperty otherProperty )
        {
            // we always check if this class is in child classes on output property type
            return await otherProperty.IsCompatibleWithAsync( this );
        }

        public bool IsArray()
        {
            return false;
        }

        public bool IsObject()
        {
            return false;
        }

        public bool IsBinding()
        {
            return false;
        }

        #endregion IProperty implementation
    }
}
